package cn.walls1717.juc.cas;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

/**
 * 4中原子操作的性能测试
 *
 * @author walls1717
 * @create 2022/12/12 20:27
 **/
@Slf4j
public class ClickNumDemo {

    static final int num = 10000;
    static final int threadNum = 50;

    public static void main(String[] args) throws InterruptedException {

        long start;
        long end;

        CountDownLatch countDownLatch1 = new CountDownLatch(threadNum);
        CountDownLatch countDownLatch2 = new CountDownLatch(threadNum);
        CountDownLatch countDownLatch3 = new CountDownLatch(threadNum);
        CountDownLatch countDownLatch4 = new CountDownLatch(threadNum);

        ClickNumber clickNumber = new ClickNumber();

        // sync
        start = System.currentTimeMillis();
        for (int i = 1; i <= threadNum; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= num * 100; j++) {
                        clickNumber.clickBySynchronized();
                    }
                } finally {
                    countDownLatch1.countDown();
                }
            }).start();
        }
        countDownLatch1.await();
        end = System.currentTimeMillis();
        log.info("sync cost " + (end - start) + " result is " + clickNumber.num);

        // atomicInteger
        start = System.currentTimeMillis();
        for (int i = 1; i <= threadNum; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= num * 100; j++) {
                        clickNumber.clickByAtomicInteger();
                    }
                } finally {
                    countDownLatch2.countDown();
                }
            }).start();
        }
        countDownLatch2.await();
        end = System.currentTimeMillis();
        log.info("AtoInteger cost " + (end - start) + " result is " + clickNumber.atomicInteger.get());

        // longAdder
        start = System.currentTimeMillis();
        for (int i = 1; i <= threadNum; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= num * 100; j++) {
                        clickNumber.clickByLongAdder();
                    }
                } finally {
                    countDownLatch3.countDown();
                }
            }).start();
        }
        countDownLatch3.await();
        end = System.currentTimeMillis();
        log.info("LongAdder cost " + (end - start) + " result is " + clickNumber.longAdder.sum());

        // longAcc
        start = System.currentTimeMillis();
        for (int i = 1; i <= threadNum; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <= num * 100; j++) {
                        clickNumber.clickByLongAcc();
                    }
                } finally {
                    countDownLatch4.countDown();
                }
            }).start();
        }
        countDownLatch4.await();
        end = System.currentTimeMillis();
        log.info("LongAcc cost " + (end - start) + " result is " + clickNumber.longAccumulator.get());
    }
}

class ClickNumber {
    int num = 0;

    public synchronized void clickBySynchronized() {
        num++;
    }

    AtomicInteger atomicInteger = new AtomicInteger(0);
    public void clickByAtomicInteger() {
        atomicInteger.getAndIncrement();
    }

    LongAdder longAdder = new LongAdder();
    public void clickByLongAdder() {
        longAdder.increment();
    }

    LongAccumulator longAccumulator = new LongAccumulator(Long::sum, 0);
    public void clickByLongAcc() {
        longAccumulator.accumulate(1);
    }
}
